package locks

import (
	"gitee.com/jade-virtual/kunlun/component/cache"
	"gitee.com/jade-virtual/kunlun/pkg/runtime"
	"sync"
)

type GlobalLock struct {
	goGlobalMap sync.Map
	redisLock   *RedisLock
}

func NewGlobalLock(store cache.Store) *GlobalLock {
	return &GlobalLock{redisLock: NewRedisLock(store)}
}

func (g *GlobalLock) set(key, v interface{}) {
	g.goGlobalMap.Store(key, v)
}

func (g *GlobalLock) get(key interface{}) interface{} {
	if v, ok := g.goGlobalMap.Load(key); ok {
		return v
	}
	return nil
}

func (g *GlobalLock) del(k interface{}) {
	g.goGlobalMap.Delete(k)
}

func (g *GlobalLock) getLocalLocks(goId uint64) map[string]uint64 {
	value := g.get(goId)
	if value == nil {
		return make(map[string]uint64, 0)
	} else {
		return value.(map[string]uint64)
	}
}

func (g *GlobalLock) setLocalLocks(goId uint64, v map[string]uint64) {
	if v == nil || len(v) == 0 {
		// 清空列表
		g.del(goId)
	} else {
		// 设置列表
		g.set(goId, v)
	}
}

func (g *GlobalLock) Lock(ids ...string) {
	goId := runtime.SelfRuntime.GoId()
	localLocks := g.getLocalLocks(goId)
	for _, id := range ids {
		if _, ok := localLocks[id]; !ok {
			g.redisLock.SyncLock(id, goId)
			localLocks[id] = goId
		}
	}
	g.setLocalLocks(goId, localLocks)
}

func (g *GlobalLock) GetLock(ids ...string) bool {
	goId := runtime.SelfRuntime.GoId()
	localLocks := g.getLocalLocks(goId)
	for _, id := range ids {
		if _, ok := localLocks[id]; !ok {
			flag := g.redisLock.GetLock(id, goId)
			if !flag {
				return flag
			}
			localLocks[id] = goId
		}
	}
	g.setLocalLocks(goId, localLocks)
	return true
}

func (g *GlobalLock) Unlock(ids ...string) {
	goId := runtime.SelfRuntime.GoId()
	localLocks := g.getLocalLocks(goId)
	for _, id := range ids {
		if _, ok := localLocks[id]; ok {
			g.redisLock.Unlock(id, goId)
		}
	}
	for _, id := range ids {
		delete(localLocks, id)
	}
	g.setLocalLocks(goId, localLocks)
}

func (g *GlobalLock) UnlockAll() {
	goId := runtime.SelfRuntime.GoId()
	localLocks := g.getLocalLocks(goId)
	for id, _ := range localLocks {
		g.redisLock.Unlock(id, goId)
	}
	g.setLocalLocks(goId, nil)
}
